/*******************************************************************************
 * Copyright (c) 2000, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package com.architexa.org.eclipse.gef.tools;

import com.architexa.org.eclipse.gef.DragTracker;
import com.architexa.org.eclipse.gef.EditPart;
import com.architexa.org.eclipse.gef.EditPartViewer;
import com.architexa.org.eclipse.gef.Request;
import com.architexa.org.eclipse.gef.RequestConstants;
import com.architexa.org.eclipse.gef.requests.DirectEditRequest;
import com.architexa.org.eclipse.gef.requests.SelectionRequest;

import java.util.List;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Cursor;


/**
 * DragTracker used to select, edit, and open {@link com.architexa.org.eclipse.gef.EditPart EditParts}.
 */
public class SelectEditPartTracker
	extends TargetingTool
	implements DragTracker
{

/** Flag to indicate selection has been performed. */
protected static final int FLAG_SELECTION_PERFORMED = TargetingTool.MAX_FLAG << 1;
private static final int FLAG_ENABLE_DIRECT_EDIT = TargetingTool.MAX_FLAG << 2;
/** Max flag */
protected static final int MAX_FLAG = FLAG_ENABLE_DIRECT_EDIT;

private EditPart editpart;

/**
 * Constructs a new SelectEditPartTracker with the given edit part as the source.
 * @param owner the source edit part
 */
public SelectEditPartTracker(EditPart owner) {
	setSourceEditPart(owner);
}

/**
 * @see com.architexa.org.eclipse.gef.tools.AbstractTool#calculateCursor()
 */
protected Cursor calculateCursor() {
	if (isInState(STATE_INITIAL | STATE_DRAG | STATE_ACCESSIBLE_DRAG))
		return getDefaultCursor();
	return super.calculateCursor();
}

/**
 * @see com.architexa.org.eclipse.gef.tools.AbstractTool#getCommandName()
 */
protected String getCommandName() {
	return "Select Tracker";//$NON-NLS-1$
}

/**
 * @see com.architexa.org.eclipse.gef.tools.AbstractTool#getDebugName()
 */
protected String getDebugName() {
	return "Select Tracker";//$NON-NLS-1$
}

/**
 * Returns the source edit part.
 * @return the source edit part
 */
protected EditPart getSourceEditPart() {
	return editpart;
}

/**
 * Performs a conditional selection if needed (if right or left mouse button have been 
 * pressed) and goes into the drag state. If any other button has been pressed, the tool 
 * goes into the invalid state.
 * @see com.architexa.org.eclipse.gef.tools.AbstractTool#handleButtonDown(int)
 */
protected boolean handleButtonDown(int button) {
	if ((button == 3 || button == 1)
	  && isInState(STATE_INITIAL))
		performConditionalSelection();

	if (button != 1) {
		setState(STATE_INVALID);
		if (button == 3)
			setState(STATE_TERMINAL);
		handleInvalidInput();
	} else
		stateTransition(STATE_INITIAL, STATE_DRAG);
	return true;
}

/**
 * If in the drag state, the tool selects the source edit part.  If the edit part was 
 * already selected, {@link #performDirectEdit()} is called.  If the edit part is newly
 * selected and not completely visible, {@link EditPartViewer#reveal(EditPart)} is 
 * called to show the selected edit part.
 * @see com.architexa.org.eclipse.gef.tools.AbstractTool#handleButtonUp(int)
 */
protected boolean handleButtonUp(int button) {
	if (isInState(STATE_DRAG)) {
		performSelection();
		if (getFlag(FLAG_ENABLE_DIRECT_EDIT))
			performDirectEdit();
		if (button == 1 && getSourceEditPart().getSelected() != EditPart.SELECTED_NONE)
			getCurrentViewer().reveal(getSourceEditPart());
		setState(STATE_TERMINAL);
		return true;
	}
	return false;
}

/**
 * Calls {@link #performOpen()} if the double click was with mouse button 1.
 * @see com.architexa.org.eclipse.gef.tools.AbstractTool#handleDoubleClick(int)
 */
protected boolean handleDoubleClick(int button) {
	setFlag(FLAG_ENABLE_DIRECT_EDIT, false);
	if (button == 1) {
		//Prevent selection from happening later on mouse up
		setFlag(FLAG_SELECTION_PERFORMED, true);
		performOpen();
	}
	return true;
}

/**
 * @see com.architexa.org.eclipse.gef.tools.AbstractTool#handleDragStarted()
 */
protected boolean handleDragStarted() {
	return stateTransition(STATE_DRAG, STATE_DRAG_IN_PROGRESS);
}

/**
 * Returns <code>true</code> if selection has already occured.
 * @return <code>true</code> if selection has occured
 */
protected boolean hasSelectionOccurred() {
	return getFlag(FLAG_SELECTION_PERFORMED);
}

/**
 * Calls {@link #performSelection()} if the source is not selected.  If the source is 
 * selected and there are no modifier keys pressed (i.e. the user isn't selecting
 * multiple edit parts or deselecting edit parts), sets the direct edit flag so that when
 * the mouse is released, a direct edit will be performed.
 */
protected void performConditionalSelection() {
	if (getSourceEditPart().getSelected() == EditPart.SELECTED_NONE)
		performSelection();
	else if (getCurrentInput().getModifiers() == 0)
		setFlag(FLAG_ENABLE_DIRECT_EDIT, true);
}

/**
 * Creates a {@link DirectEditRequest} and sends it to a DelayedDirectEditHelper
 * to allow the user to directly edit the edit part.
 */
protected void performDirectEdit() {
	DirectEditRequest req = new DirectEditRequest();
	req.setLocation(getCurrentInput().getMouseLocation());
	new DelayedDirectEditHelper(
		getSourceEditPart().getViewer(),
		req,
		getSourceEditPart());
}

/**
 * Creates a {@link SelectionRequest} and sends it to the source edit part via
 * {@link EditPart#performRequest(Request)}.  Possible uses are to open the selected item
 * in another editor or replace the current editor's contents based on the selected item.
 */
protected void performOpen() {
	SelectionRequest request = new SelectionRequest();
	request.setLocation(getLocation());
	request.setModifiers(getCurrentInput().getModifiers());
	request.setType(RequestConstants.REQ_OPEN);
	getSourceEditPart().performRequest(request);
}

/**
 * Performs the appropriate selection action based on the selection state of the source
 * and the modifiers (CTRL and SHIFT).  If no modifier key is pressed, the source will be
 * set as the only selection.  If the CTRL key is pressed and the edit part is already
 * selected, it will be deselected.  If the CTRL key is pressed and the edit part is not
 * selected, it will be appended to the selection set.  If the SHIFT key is pressed, the
 * source will be appended to the selection.
 */
protected void performSelection() {
	if (hasSelectionOccurred())
		return;
	setFlag(FLAG_SELECTION_PERFORMED, true);
	EditPartViewer viewer = getCurrentViewer();
	List selectedObjects = viewer.getSelectedEditParts();

	if (getCurrentInput().isModKeyDown(SWT.MOD1)) {
		if (selectedObjects.contains(getSourceEditPart()))
			viewer.deselect(getSourceEditPart());
		else
			viewer.appendSelection(getSourceEditPart());
	} else if (getCurrentInput().isShiftKeyDown())
		viewer.appendSelection(getSourceEditPart());
	else
		viewer.select(getSourceEditPart());
}

/**
 * @see com.architexa.org.eclipse.gef.tools.AbstractTool#resetFlags()
 */
protected void resetFlags() {
	super.resetFlags();
	setFlag(FLAG_SELECTION_PERFORMED, false);
	setFlag(FLAG_ENABLE_DIRECT_EDIT, false);
}

/**
 * Sets the source edit part.
 * @param part the source edit part
 */
protected void setSourceEditPart(EditPart part) {
	this.editpart = part;
}

}
